package dpss.server;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.SocketException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.TransformerException;

import org.omg.CORBA.ORB;
import org.omg.CosNaming.NameComponent;
import org.omg.CosNaming.NamingContextExt;
import org.omg.CosNaming.NamingContextExtHelper;
import org.omg.PortableServer.POA;
import org.omg.PortableServer.POAHelper;
import org.xml.sax.SAXException;

import dpss.commons.CustomLogger;
import dpss.commons.Player;
import dpss.commons.Repo;
import dpss.server.udp.UDPListenerThread;
import dpss_api_corba.DpssApi;
import dpss_api_corba.DpssApiHelper;

/**
 * The basic class to start the server
 * 
 * @author musab.mirza
 * 
 */
public class Server {
	
	private final String FILE_PROPERTIES = "project.properties";

	private CustomLogger logger = CustomLogger.getInstance();
	private static Properties prop = null;
	private AccountDatabase accountDatabase;
	private static Repo repo;
	

	/**
	 * Start it's own instance
	 * 
	 * @param args
	 */
	public static void main(String[] args){
		try {
			new Server();
		} catch (ParserConfigurationException | SAXException | IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * Start of the program
	 * 
	 * @param args
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	private Server() throws ParserConfigurationException,
			SAXException, IOException {

		logger.logConsole(true);

		loadProperties();
		logger.log("\n************************************************\n" + prop.getProperty("server.name") + " started!\n************************************************\n");

		repo = new Repo(prop.getProperty("repo.path"));

		updateRepo();

		TestData testData = new TestData();
		HashSet<Player> playersList = testData.getTestPlayers(prop
				.getProperty("server.iprange"));

		accountDatabase = new AccountDatabase();
		accountDatabase.loadTestPlayers(playersList);

		setupUDPListener();
		setupCORBAService(prop.getProperty("server.id"));
		
	}

	/**
	 * Sets up the CORBA service
	 * @param string 
	 */
	private void setupCORBAService(String serverId) {

		logger.log("Starting CORBA service with args: " + prop.getProperty("corba.args"));
		String[] corbaArgs = prop.getProperty("corba.args").split("\\s+");
		
		try {
			ORB orb = ORB.init(corbaArgs, null);
			POA rootpoa = POAHelper.narrow(orb .resolve_initial_references("RootPOA"));
			rootpoa.the_POAManager().activate();
			DpssApiImpl dpssApiImpl = new DpssApiImpl(accountDatabase);
			dpssApiImpl.setORB(orb);
			org.omg.CORBA.Object ref = rootpoa
					.servant_to_reference(dpssApiImpl);
			DpssApi href = DpssApiHelper.narrow(ref);
			org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
			NamingContextExt ncRef = NamingContextExtHelper.narrow(objRef);
			
			String name = "GameServer_" + serverId;
			NameComponent path[] = ncRef.to_name(name);
			ncRef.rebind(path, href);
			orb.run();
		}
		catch (Exception e) {
			System.err.println("ERROR: " + e);
			e.printStackTrace(System.out);
		}
	}

	/**
	 * Loads the properties file into the cache
	 */
	private void loadProperties() {
		prop = new Properties();
		InputStream input = null;
		try {
			input = new FileInputStream(FILE_PROPERTIES);
			prop.load(input);
		} catch (IOException ex) {
			logger.log("ERROR - Unable to update repo!");
		} finally {
			if (input != null) {
				try {
					input.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * Starts the UDP listener thread
	 */
	private void setupUDPListener() {
		try {
			UDPListenerThread UDPListener = new UDPListenerThread(
					accountDatabase, Integer.parseInt((String) prop
							.getProperty("server.udpport")));
			UDPListener.start();
		} catch (NumberFormatException e) {
			e.printStackTrace();
		} catch (SocketException e) {
			logger.log("ERROR - Could not start UDP listener on port "
					+ prop.getProperty("server.udpport"));
			System.exit(0);
		}
	}


	/**
	 * Loads information from the properties file and writes it to the central
	 * repository
	 * 
	 * @throws TransformerException
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	private void updateRepo() {

		// Building the map
		Map<String, String> serverInfo = new HashMap<String, String>();
		serverInfo.put("id", prop.getProperty("server.id"));
		serverInfo.put("name", prop.getProperty("server.name"));
		serverInfo.put("udpport", prop.getProperty("server.udpport"));
		serverInfo.put("iprange", prop.getProperty("server.iprange"));
		try {
			repo.setServerInfo(serverInfo);
		} catch (ParserConfigurationException | SAXException | IOException
				| TransformerException e) {
			logger.log("ERROR - Unable to update the repo");
			e.printStackTrace();
			System.exit(0);
		}
		logger.log("Repo updated with server info");
	}

	/**
	 * Returns the current server's properties
	 * 
	 * @return
	 */
	public static Properties getServerProperties() {
		return prop;
	}

	/**
	 * Returns the repo object's instance
	 * 
	 * @return
	 */
	public static Repo getRepo() {
		return repo;
	}

}
